package cn.wujiangbo.service.impl;

import cn.wujiangbo.Thread.AddUserRunnable;
import cn.wujiangbo.domain.User;
import cn.wujiangbo.mapper.UserMapper;
import cn.wujiangbo.service.IUserService;
import cn.wujiangbo.utils.MyTools;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 用户信息表 服务实现类
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public void deleteAllUser() {
        userMapper.deleteAllUser();
    }

    /**
     * 这里批量添加数据有三种方式
     * 1、foreach批量插入（性能最好）
     * 2、mybatis batch模式插入
     * 3、for循环insert
     */
    @Override
    public void addUser(int count) {
        if(count > 0){
            try {
                long start = System.currentTimeMillis();
                List<User> userList = MyTools.getTestUserList(count, false);
                //数据太多需要分批次提交，这里以1000条提交一次为例
                int countItem = 1000;//每次提交的记录条数
                int userSize = userList.size();//10万
                List<User> userListTemp = new ArrayList<>();
                for (int i = 0, n=userSize; i < n; i++) {
                    User user= userList.get(i);
                    userListTemp.add(user);
                    if ((i>0 && i % countItem == 0) || i == userSize - 1) {
                        //每1000条记录提交一次
                        userMapper.insertBatch(userListTemp);
                        userListTemp.clear();
                    }
                }
                long end = System.currentTimeMillis();
                System.out.println("批量插入数据" + count + "条，总耗时：" + (end - start) + "毫秒");
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    private static int corePoolSize = Runtime.getRuntime().availableProcessors();

    //自定义线程池
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            corePoolSize, //指定核心线程数量
            2 * corePoolSize, //指定最大线程数
            10L, //指定线程空闲后的最大存活时间
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(), //任务较多时暂存到此队列（无界队列）
            new ThreadPoolExecutor.DiscardPolicy() //策略：丢弃任务
    );

    //多线程方式添加数据
    @Override
    public void addUserThreadPool(int count) {//1000000
        if(count > 0){
            List<User> userList = MyTools.getTestUserList(count, false);
            long t1 = System.currentTimeMillis();
            /**
             * 分批交给线程去处理
             * 必须要保重最终入库数据是count条
             */
            int dataLimit = 50000;//每批次的限制条数
            int size = userList.size();
            int runSize = size / dataLimit;//分批数20
            System.out.println("共有数据" + size + "条，" + "分 " + runSize + " 个线程去操作");
            CountDownLatch latch = new CountDownLatch(size);

            List<User> newList = null;
            for (int i = 0; i < runSize; i++) {
                if((i+1)==runSize){
                    int startIndex = (i*dataLimit);;
                    int endIndex = userList.size();
                    newList = userList.subList(startIndex,endIndex);
                }else{
                    int startIndex = i*dataLimit;
                    int endIndex = (i+1)*dataLimit;
                    newList = userList.subList(startIndex,endIndex);
                }
                /**
                 * 例子：count：一百万，dataLimit：五万，runSize：20
                 * 第一次：startIndex=0，endIndex=5万
                 * 第一次：startIndex=5万，endIndex=10万
                 * ...
                 * 第20次：startIndex=95万，endIndex=100万，此时i等于19
                 * 循环结束
                 */
                executor.execute(new AddUserRunnable(userMapper, newList, latch));
            }
            try {
                latch.await();//等待所有线程执行完成之后，再统计执行时间
                executor.shutdownNow();
                long t2 = System.currentTimeMillis();
                System.out.println("总耗时：" + (t2-t1) + "毫秒");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
